package com.xy.job.admin.controller

import com.xy.job.admin.api.response.MateDTO
import com.xy.job.admin.core.notify.NotifyChannel
import com.xy.job.admin.enums.*
import com.xy.job.core.api.response.wrapperResponse
import com.xy.job.core.common.IDict
import com.xy.job.core.common.Mate
import com.xy.job.core.enums.JobStatus
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.context.MessageSource
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import java.util.*
import kotlin.reflect.KClass
import kotlin.reflect.KProperty1
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.memberProperties

@Tag(name = "MateApi")
@Validated
@RestController
@RequestMapping("/api/mate")
class MateController(
    private var messageSource: MessageSource
) {

    private val enumMateMap: Map<String, KClass<*>> = listOf(
        ScheduleType::class,
        JobStatus::class,
        ExecuteMode::class,
        NotifyChannelType::class,
        NotifyScene::class,
        NotifyStatus::class,
        NodeType::class,
    ).associateBy { it.simpleName!!.lowerCamelCase() }

    private val classMateMap: Map<String, KClass<*>> = listOf(
        NotifyChannel.DingTalkRobot::class,
    ).associateBy { it.simpleName!!.lowerCamelCase() }

    @Operation(summary = "查询所有元数据")
    @GetMapping("/all")
    fun listAllMates() = wrapperResponse {
        val enumMates = enumMateMap.keys.mapNotNull { buildMateByEnum(it) }
        val classMates = classMateMap.keys.mapNotNull { buildMateByClass(it) }
        sequenceOf(enumMates, classMates)
            .flatten()
            .toList()
    }

    @Operation(summary = "查询元数据")
    @GetMapping("/{mateKeys}")
    fun listMates(@PathVariable("mateKeys") vararg mateKeys: String) = wrapperResponse {
        mateKeys.mapNotNull { buildMateByEnum(it) ?: buildMateByClass(it) }
    }

    fun buildMateByEnum(mateKey: String): MateDTO? {
        val mateClass = enumMateMap[mateKey] ?: return null
        val mateAnnotation = mateClass.findAnnotation<Mate>()!!
        val options = mateClass.java.enumConstants.map { buildOption(it as IDict) }
        return MateDTO(
            key = mateKey,
            displayName = messageSource.getMessage(
                mateAnnotation.displayNameI18nKey, null,
                mateAnnotation.displayNameDefaultValue, Locale.getDefault()
            ),
            options = options
        )
    }

    private fun buildOption(iDict: IDict): MateDTO {
        return MateDTO(
            displayName = messageSource.getMessage(
                iDict.displayNameI18nKey(), null,
                iDict.displayNameDefaultValue(), Locale.getDefault()
            ),
            key = iDict.key()
        )
    }

    private fun buildMateByClass(mateKey: String): MateDTO? {
        val mateClass = classMateMap[mateKey] ?: return null
        val mateAnnotation: Mate = mateClass.findAnnotation<Mate>() ?: return null
        return MateDTO(
            key = mateKey,
            displayName = messageSource.getMessage(
                mateAnnotation.displayNameI18nKey, null,
                mateAnnotation.displayNameDefaultValue, Locale.getDefault()
            ),
            options = buildOptions(mateClass)
        )
    }

    private fun buildOptions(mateClass: KClass<*>): List<MateDTO> {
        return mateClass.memberProperties.map { buildOption(it) }
    }

    private fun buildOption(it: KProperty1<out Any, *>): MateDTO {
        val mateAnnotation: Mate? = it.findAnnotation<Mate>()
        return if (mateAnnotation != null) {
            MateDTO(
                displayName = messageSource.getMessage(
                    mateAnnotation.displayNameI18nKey, null,
                    mateAnnotation.displayNameDefaultValue, Locale.getDefault()
                ),
                key = it.name
            )
        } else {
            MateDTO(
                displayName = it.name,
                key = it.name,
            )
        }
    }

    fun String.lowerCamelCase(): String {
        if (this.length <= 1) {
            return this
        }
        return this.substring(0, 1).lowercase() + this.substring(1)
    }
}